using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Sublimation
{
    public class ScriptedEventMages : ScriptedEvent
    {
        public ScriptedEventMages(WorldProperties worldProperties)
            : base(worldProperties)
        {
        }

        protected const double SCRIPT_START_TIME_SECONDS = 140;

        protected const int APPEAR_NUMBER_OF_FRAMES_TO_PLAY = 8;
        protected const int APPEAR_FRAMES_PER_SECOND = 6;
        protected const int TOTAL_WIZARD_GROUPS = 4;
        protected const int WIZARDS_PER_GROUP = 5;
        protected const float SPAWN_DRIFT_RADIUS = 50f;
        Vector2[] groupPosition = new Vector2[TOTAL_WIZARD_GROUPS];
        Vector2[] spawnPosition = new Vector2[TOTAL_WIZARD_GROUPS * WIZARDS_PER_GROUP];
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // 2:20 - mages
            if (state == 0)
            {
                if (gameTime.TotalGameTime.Subtract(worldProperties.PauseTime).TotalSeconds >= SCRIPT_START_TIME_SECONDS) state = 1;

            }
            else if (state == 1)
            {
                // write the text warning of his approach
                worldProperties.DrawTextWarning("Expelliarmus!", ScriptedEvent.DEFAULT_WARNING_TEXT_MILLIS);
                state = 2;

            }
            else if (state == 2)
            {
                if (gameTime.TotalGameTime.Subtract(worldProperties.PauseTime).TotalSeconds >= SCRIPT_START_TIME_SECONDS + 1) state = 3;

            }
            else if (state == 3)
            {
                // show the appearing animation/effect
                for (int i = 0; i < TOTAL_WIZARD_GROUPS; i++)
                {
                    groupPosition[i] = GetSpawnPosition(90);
                    for (int j = 0; j < WIZARDS_PER_GROUP; j++)
                    {
                        Vector2 spawnDrift = new Vector2((float)Math.Cos(worldProperties.Random.NextDouble()), (float)Math.Sin(worldProperties.Random.NextDouble())) * SPAWN_DRIFT_RADIUS;
                        spawnPosition[i * WIZARDS_PER_GROUP + j] = groupPosition[i] + spawnDrift;
                        // play the teleport animation
                        new Animation(worldProperties,
                            spawnPosition[i * WIZARDS_PER_GROUP + j], 0, new Vector2(90, 90), 1.0f,
                            APPEAR_NUMBER_OF_FRAMES_TO_PLAY, APPEAR_FRAMES_PER_SECOND, "Sprites\\scripted_mages_appear");
                    }
                }
                
                state = 4;

            }
            else if (state == 4)
            {
                if (gameTime.TotalGameTime.Subtract(worldProperties.PauseTime).TotalSeconds >= SCRIPT_START_TIME_SECONDS + 1.5) state = 5;

            }
            else if (state == 5)
            {
                for (int i = 0; i < TOTAL_WIZARD_GROUPS * WIZARDS_PER_GROUP; i++)
                {
                    // spawn the unit
                    EnemyUnitWizard unitWizard = new EnemyUnitWizard(worldProperties, EnemyKind.Wizard);
                    // give it a spawn position
                    unitWizard.Position = spawnPosition[i];
                    // set the rotation to face them in towards the map
                    Vector2 vectorToCenter = new Vector2(worldProperties.Map.WorldBounds.Center.X, worldProperties.Map.WorldBounds.Center.Y) - unitWizard.Position;
                    unitWizard.Rotation = (float)Math.Atan2(vectorToCenter.Y, vectorToCenter.X);
                    worldProperties.Enemies.Add(unitWizard);
                }

                state = 6;
            }
            else if (state == 6)
            {
                Game.Components.Remove(this);
            }
        }

        public const float CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS = 6.0f;
        public Vector2 GetSpawnPosition(float distanceFromEdge)
        {
            Vector2 vectorToCenter;
            Vector2 PerimeterVector;
            do
            {
                // get a position on the edge of the map
                int perimeterPosition = worldProperties.Random.Next(0, worldProperties.Map.Perimeter);
                if (perimeterPosition <= worldProperties.Map.WorldBounds.Width)
                {
                    // place on the top
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + 1);
                }
                else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height)
                {
                    // right side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + worldProperties.Map.WorldBounds.Width - 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
                }
                else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height)
                {
                    // bottom side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + worldProperties.Map.WorldBounds.Height - 1);
                }
                else
                {
                    // left side
                    perimeterPosition -= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height;
                    PerimeterVector = new Vector2(worldProperties.Map.WorldBounds.X + 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
                }

                // now get a vecotr from the perimeter to the center
                vectorToCenter = new Vector2(worldProperties.Map.WorldBounds.Center.X, worldProperties.Map.WorldBounds.Center.Y) - PerimeterVector;

                // shrink the vector to remove collisions with bad spawn locations
                foreach (Player player in worldProperties.Players)
                {
                    foreach (Tower tower in player.Towers)
                    {
                        // do we collide with this tower?
                        if (Vector2.Distance(PerimeterVector + vectorToCenter, tower.Position) <= Tower.RADIUS * CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS)
                        {
                            // remove the amount of collision from the vector length
                            float distanceToEdgeOfCollision = Vector2.Distance(PerimeterVector, tower.Position) - Tower.RADIUS * CLOSEST_SPAWN_TO_TOWER_AS_FACTOR_OF_RADIUS;
                            if (distanceToEdgeOfCollision <= 0) distanceToEdgeOfCollision = vectorToCenter.Length();
                            vectorToCenter = Vector2.Normalize(vectorToCenter) * (vectorToCenter.Length() - distanceToEdgeOfCollision);
                        }
                    }
                }
            } while (vectorToCenter.Length() <= SPAWN_DRIFT_RADIUS);

            // now we should have a vector from the edge to a point in the direction of the center
            // anywhere on this is a valid spawn point
            //XXX: havent taken collision radius of spawn point into account when working out the vector

            // get a random position on the vector
            float spawnPosition = (float)worldProperties.Random.NextDouble() * (vectorToCenter.Length() - SPAWN_DRIFT_RADIUS) + SPAWN_DRIFT_RADIUS;
            // scale the vector to this position
            vectorToCenter = Vector2.Normalize(vectorToCenter) * spawnPosition;
            // get this as a position from the perimeter and set Position
            return PerimeterVector + vectorToCenter;
        }
    }
}